﻿using LettuceEncrypt.Acme;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Quickly.Certs.AliDns;

namespace Quickly.Certs.LettuceEncrypt
{
    public class Dns01ChallengeProvider : IDnsChallengeProvider
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly ILogger _logger;

        public Dns01ChallengeProvider(
            IServiceProvider serviceProvider,
            ILogger<Dns01ChallengeProvider> logger)
        {
            _serviceProvider = serviceProvider;
            _logger = logger;
        }

        public IAlibabaCloudDomainRecordRepository GetAlibabaCloudDomainRecordRepository(IServiceProvider serviceProvider) => serviceProvider.GetRequiredService<IAlibabaCloudDomainRecordRepository>();
        public AlibabaCloudDnsClient GetAlibabaCloudDnsClient(IServiceProvider serviceProvider) => serviceProvider.GetRequiredService<AlibabaCloudDnsClient>();

        public async Task<DnsTxtRecordContext> AddTxtRecordAsync(string domainName, string txt, CancellationToken ct = default)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var _repository = GetAlibabaCloudDomainRecordRepository(scope.ServiceProvider);
                var _dnsClient = GetAlibabaCloudDnsClient(scope.ServiceProvider);

                _logger.LogInformation("Dns01 Challenge: {DomainName}", domainName);

                var (rr, rootDomainName) = SplitDomainName(domainName);

                var recordId = await _dnsClient.AddDomainRecordAsync(rootDomainName, rr, DomainRecordType.TXT, txt, ct);

                if (recordId == null)
                {
                    throw new InvalidOperationException("Create Domain Record Failed");
                }

                await _repository.CreateOrUpdateAsync(domainName, DomainRecordType.TXT, txt, recordId, ct);

                return new DnsTxtRecordContext(domainName, txt);
            }
        }

        private (string rr, string rootDomainName) SplitDomainName(string domainName)
        {
            var spans = domainName.Split('.').AsSpan<string>();

            var index = spans.Length - 2;

            var rr = string.Join(".", spans[..index]!);
            var domain = string.Join(".", spans[index..]!);

            return (rr, domain);
        }

        public async Task RemoveTxtRecordAsync(DnsTxtRecordContext context, CancellationToken ct)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var _repository = GetAlibabaCloudDomainRecordRepository(scope.ServiceProvider);
                var _dnsClient = GetAlibabaCloudDnsClient(scope.ServiceProvider);

                var recordId = await _repository.GetRecordIdAsync(context.DomainName, DomainRecordType.TXT, ct);

                if (recordId == null)
                {
                    throw new InvalidOperationException($"Cannot found record for domain:'{context.DomainName}' type:'{DomainRecordType.TXT}'");
                }

                var deletedRecordId = await _dnsClient.DeleteDomainRecordAsync(recordId, ct);

                if (deletedRecordId != null)
                {
                    await _repository.DeleteDomainRecordAsync(context.DomainName, DomainRecordType.TXT, deletedRecordId, ct);
                    _logger.LogInformation("DomainRecord deleted. domain:{Domain}, type:{DomainRecordType}, recordId:{RecordId}", context.DomainName, DomainRecordType.TXT, deletedRecordId);
                }
            }
        }
    }
}
